/*
 * SAXSerializer.java
 *
 * Created on July 12, 2006, 11:24 AM
 *
 * (C) R. Alexander Milowski alex@milowski.com
 */

package org.infoset.xml.sax;

import java.net.URI;
import java.util.ArrayList;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.Serializer;
import org.xml.sax.ContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;

/**
 *
 * @author R. Alexander Milowski
 */
public class SAXSerializer implements Serializer {
   
   static class SAXAttribute {
      String qname;
      Name name;
      String value;
      SAXAttribute(String prefix,Name name,String value) {
         this.qname = toQName(prefix,name);
         this.name = name;
         this.value = value;
      }
   }
   
   static String toQName(String prefix,Name name)
   {
      return prefix==Name.NO_PREFIX || prefix.equals(Name.NO_PREFIX) ? name.getLocalName() : prefix+':'+name.getLocalName();
   }

   class AttributeList extends ArrayList<SAXAttribute> implements Attributes {
      public String getValue(String qname) {
         for (SAXAttribute att : this) {
            if (att.qname.equals(qname)) {
               return att.value;
            }
         }
         return null;
      }

      public String getType(String qname) {
         return "CDATA";
      }

      public int getIndex(String qname) {
         int len = size();
         for (int i=0; i<len; i++) {
            SAXAttribute att = get(i);
            if (att.qname.equals(qname)) {
               return i;
            }
         }
         return -1;
      }

      public String getValue(int index) {
         SAXAttribute att = get(index);
         return att==null ? null : att.value;
      }

      public String getURI(int index) {
         SAXAttribute att = get(index);
         return att==null ? null : att.name.getNamespaceName()==Name.NO_NAMESPACE ? "" : att.name.getNamespaceName().toString();
      }

      public String getType(int index) {
         return "CDATA";
      }

      public String getQName(int index) {
         SAXAttribute att = get(index);
         return att==null ? null : att.qname;
      }

      public String getLocalName(int index) {
         SAXAttribute att = get(index);
         return att==null ? null : att.name.getLocalName();
      }

      public String getValue(String uri, String localName) {
         for (SAXAttribute att : this) {
            if (att.name.getNamespaceName().toString().equals(uri) && att.name.getLocalName().equals(localName)) {
               return att.value;
            }
         }
         return null;
      }

      public String getType(String uri, String localName) {
         return "CDATA";
      }

      public int getLength() {
         return size();
      }

      public int getIndex(String uri, String localName) {
         int len = size();
         for (int i=0; i<len; i++) {
            SAXAttribute att = get(i);
            if (uri.length()==0 && att.name.getNamespaceName()==Name.NO_NAMESPACE && att.name.getLocalName().equals(localName)) {
               return i;
            } else if (att.name.getNamespaceName().toString().equals(uri) && att.name.getLocalName().equals(localName)) {
               return i;
            }
         }
         return -1;
      }
      
   }
   protected ContentHandler handler;
   protected AttributeList attributes;
   protected LexicalHandler lexHandler;
   protected String elementPrefix;
   
   /** Creates a new instance of SAXSerializer */
   public SAXSerializer() {
      this.handler = null;
      this.attributes = new AttributeList();
   }
   
   public void startDocument()
      throws XMLException
   {
      try {
         handler.startDocument();
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
   }
   
   public void endDocument()
      throws XMLException
   {
      try {
         handler.endDocument();
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
   }
   
   public void xmlDeclaration(String version,String encoding,String standalone)
      throws XMLException
   {
   }
   public void startElement(String prefix,Name name)
      throws XMLException
   {
      elementPrefix = prefix;
   }
   
   public void endStartElement(Name name)
      throws XMLException
   {
      try {
         URI namespace = name.getNamespaceName();
         handler.startElement(namespace==Name.NO_NAMESPACE ? "" : namespace.toString(),name.getLocalName(),toQName(elementPrefix,name),attributes);
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
      attributes.clear();
   }
   public void attribute(String prefix,Name name,String value)
      throws XMLException
   {
      SAXAttribute att = new SAXAttribute(prefix,name,value);
      attributes.add(att);
   }
   public void namespaceDeclaration(String prefix,URI namespace)
      throws XMLException
   {
      try {
         handler.startPrefixMapping(prefix,namespace==Name.NO_NAMESPACE ? "" : namespace.toString());
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
   }
   public void endElement(String prefix,Name name)
      throws XMLException
   {
      try {
         URI namespace = name.getNamespaceName();
         handler.endElement(namespace==Name.NO_NAMESPACE ? "" : namespace.toString(),name.getLocalName(),toQName(prefix,name));
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
   }
   public void characters(String value,boolean cdataSection)
      throws XMLException
   {
      try {
         // Horrible, just horrible... SAX stinks.
         char [] cvalue = value.toCharArray();
         if (cdataSection) {
            lexHandler.startCDATA();
         }
         handler.characters(cvalue,0,cvalue.length);
         if (cdataSection) {
            lexHandler.endCDATA();
         }
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
   }
   public void processingInstruction(Name target,String data)
      throws XMLException
   {
      try {
         handler.processingInstruction(target.getLocalName(),data);
      } catch (SAXException ex) {
         throw new XMLException(ex.getMessage(),ex);
      }
   }
   public void comment(String data)
      throws XMLException
   {
      if (lexHandler!=null) {
         try {
            char [] cvalue = data.toCharArray();
            lexHandler.comment(cvalue,0,cvalue.length);
         } catch (SAXException ex) {
            throw new XMLException(ex.getMessage(),ex);
         }
      }
   }
   
   public void setUseCharacterReferences(boolean flag)
   {
   }
   
   public void start()
      throws XMLException
   {
   }
   
   public void end()
      throws XMLException 
   {
   }

   public ContentHandler getContentHandler() {
      return handler;
   }

   public void setHandler(ContentHandler handler) {
      this.handler = handler;
      this.lexHandler = handler instanceof LexicalHandler ? (LexicalHandler)handler : null;
   }
}
